package aupdg.logic.analysis.impl;

import aupdg.data.model.Edge;
import aupdg.data.model.Ldg;
import aupdg.data.model.Node;
import aupdg.logic.analysis.SecurityLevel;
import java.util.*;

/**
 * Find all paths in an LDG, from one node to another. All paths together is the
 * backward slice of a specific node. entryNode -> slicingCriterionNode
 *
 * @author AA and CJ
 */
public class BackwardSlice {

    private Ldg graph;
    Stack<Node> path = new Stack<>();
    Set<Node> nodesOnPath = new HashSet<>();
    /**
     * Complete backward slice of a node. Set of all paths from entryNode to a
     * node.
     */
    private List<List<Node>> backwardSlice;
    /**
     * If a and b have non-interference security levels, and a is in b's
     * backward slice there must be no path s ->* b in LDG, otherwise a security
     * leak has been discovered. Such paths are saved in this array of arrays.
     */
    private List<List<Node>> pathsWithNISecLevels;
    private List<List<String>> pathWithNISecLevelsAsString;

    /**
     * Constructor. Takes an LDG as argument.
     *
     * @param graph
     */
    public BackwardSlice(Ldg graph) {
        this.graph = graph;
        backwardSlice = new ArrayList<>();
        pathsWithNISecLevels = new ArrayList<>();
        pathWithNISecLevelsAsString = new ArrayList<>();
    }

    /**
     * Find all paths between currentNode and endNode. This method is called
     * recursively.
     *
     * @param currentNode
     * @param endNode
     */
    public void compute(Node currentNode, Node endNode) {
        boolean pathWithLeak;
        SecurityLevel prevLevel, currentLevel;
        List<Edge> adjEdges = new ArrayList<>();
        path.push(currentNode);
        nodesOnPath.add(currentNode);

        if (currentNode.getId() == endNode.getId()) {
            //save path
            //System.out.println(this.path);
            //Print path
            pathWithLeak = false;
            prevLevel = SecurityLevel.LOW;
            Iterator it = path.iterator();
            List<String> p = new ArrayList<>();
            List<Node> pn = new ArrayList<>();
            while (it.hasNext()) {
                Node n = (Node) it.next();
                currentLevel = n.getLevel();
                p.add(n.getLabel() + "(" + n.getLevel() + ")"); //For debug purposes
                pn.add(n);
                if (prevLevel.intValue > currentLevel.intValue) {
                    pathWithLeak = true;
                }
                prevLevel = currentLevel;
                //System.out.println(n.label + ",");
            }
            if (pathWithLeak) {
                pathsWithNISecLevels.add(pn);
            }
            backwardSlice.add(pn);
            System.out.println(p.toString()); //For debug purposes

        } else {
            adjEdges = this.graph.getAdjacentEdgesFromNode(currentNode);
            for (int i = 0; i < adjEdges.size(); i++) {
                if (!nodesOnPath.contains(adjEdges.get(i).getTarget())) {
                    compute(adjEdges.get(i).getTarget(), endNode);
                }
            }
        }
        path.pop();
        nodesOnPath.remove(currentNode);
    }

    /**
     * Return backward slice. Call this method after calling compute().
     *
     * @return
     */
    public List<List<Node>> getBackwardSlice() {
        return this.backwardSlice;
    }

    /**
     * Return the paths in LDG with security leaks. Call this method after
     * calling compute()
     *
     * @return
     */
    public List<List<Node>> getPathsWithNISecLevels() {
        return this.pathsWithNISecLevels;
    }
}
